﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Runtime.InteropServices;

using RSEngine.Math;
using RSEngine.Core;
using RSEngine.Helper.OGL;
using RSEngine.Helper.Interop;
using RSEngine.Tools.Selection;

using Tao.OpenGl;
using Tao.Platform.Windows;

#region Copyright (c) 2006, 2007, 2008, 2009 Render Studio Foundation. All rights reserved.

/*
 Copyright (c) 2006, 2007, 2008, 2009 Render Studio Foundation. All rights reserved.
 This work is copyrighted in its entirety. Permission to use "Render Studio"
 material may be granted only after we receive a written request from you,
 and you receive written permission from us.

 While Render Studio Foundation provides the information herein to anyone,
 we retain copyright on all code, text, graphic images, and other content.
 This means that you may not distribute the code, the text or graphics to others without the
 express written permission of Render Studio Foundation; "mirror" this information on your server
 without our permission; or modify or reuse the code, text or graphics on our servers.
 You may print copies of the information for your own personal use; and, you may reference
 the Service from your own documents. Any commercial use of this material is prohibited without
 the written permission of Render Studio Foundation. In all copies of this information, you must
 retain this notice and any other copyright notices originally included with such information.

 This material may be protected by U.S. and International Copyright laws, and you are legally
 bound by the copyright notices on those systems.

 "Render Studio" logos and other official symbols or graphics may not be used in connection with any
 product or service that has not been expressly authorized in advance in writing or in any manner
 that is likely to cause confusion among the users of this or other Web sites or among the public.
 All other seals, graphics or trademarks of organizations not affiliated with Render Studio Foundation
 that appear on this site are the property of their respective owners.

 Render Studio Foundation reserve all other rights. 
*/

#endregion

#region Render Studio Source Code Licence

/*
This license governs use of the Render Studio software. If you use the software, you accept this license. If you do not accept the license, do not use the software.

1. Summary

The source code is made available to view for reference purposes or to contribute to the project only. Developers may not distribute or modify the code for commercial or non-commercial purposes without express Agreement of Render Studio Foundation.

2. Definitions

The terms "reproduce," "reproduction," and "distribution" have the same meaning here as under U.S. copyright law.
"Licensor" means the foundation "Render Studio Foundation".
"You" means the licensee of the software, who is not engaged in designing, developing, or testing other software, that has the same or substantially the same features or functionality as the software.
"Your company" means the company you worked for when you downloaded the software.
"Reference use" means use of the software within your company as a reference, in read only form, for the sole purposes of debugging and maintaining your products. For clarity, "reference use" does NOT include (a) the right to use the software for purposes of designing, developing, or testing other software, that has the same or substantially the same features or functionality as the software, and (b) the right to distribute the software outside of your company.
"Licensed patents" means any Licensor patent claims which read directly on the software as distributed by the Licensor under this license.

3. Grant of Rights

(A) Copyright Grant- Subject to the terms of this license, the Licensor grants you a non-transferable, non-exclusive, worldwide, royalty-free copyright license to reproduce the software for reference use.
(B) Patent Grant- Subject to the terms of this license, the Licensor grants you a non-transferable, non-exclusive, worldwide, royalty-free patent license under licensed patents for reference use.
4. Limitations
(A) No Trademark License- This license does not grant you any rights to use the Licensor's name, logo, or trademarks.
(B) If you begin patent litigation against the Licensor over patents that you think may apply to the software (including a cross-claim or counterclaim in a lawsuit), your license to the software ends automatically.
(C) The software is licensed "as-is." You bear the risk of using it. The Licensor gives no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the Licensor excludes the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
 */

#endregion

namespace RSEngine.Renderer.Hardware
{
    // http://www.songho.ca/opengl/gl_vertexarray.html
    // http://paris.cs.wayne.edu/~ay2703/research/publications/SceneGraphCR.pdf
    // http://www.mesa3d.org/brianp/sig97/perfopt.htm
    sealed unsafe public class GLRenderer
    {

        #region Variables

        private GLRenderParameters _parameters;
        private Scene _scene;

        private GLModeSettings _GLModeSettings;

        //---- Colors
        static public float[] COLOR_AXIS_X = new float[] { 0f, 1f, 0f };
        static public float[] COLOR_AXIS_Y = new float[] { 1f, 0f, 0f };
        static public float[] COLOR_AXIS_Z = new float[] { 0f, 0f, 1f };

        static public float[] COLOR_WIREFRAME = new float[] { 1f, 1f, 1f };

        static public float[] COLOR_NORMAL = new float[] { 0f, 1f, 0f };

        static public float[] COLOR_LIGHT = new float[] { 0.5f, 0f, 0.5f };

        static public float[] COLOR_SHADEDMODE = new float[] { 0.6f, 0.6f, 0.6f };

        static public float[] COLOR_SELECTION = new float[] { 0.6f, 0f, 0f };

        //---- Matrices stack
        private Stack<float[]> _matrices = new Stack<float[]>();

        //---- Optimization
        /// <summary>
        /// Does the VBO are supported ?
        /// </summary>
        private bool _isVBOSupported;

        //---- Scene updates management
        /// <summary>
        /// Tell if we must update the VBO, display lists...
        /// </summary>
        private bool _refreshModel = true;

        //---- Lights
        private int _lightId;

        //---- Selection management
        private GLShadingItem _selectionColorStack;

        #endregion

        #region Render

        /// <summary>
        /// Start rendering on a target.
        /// </summary>
        /// <param name="target">The target to render to</param>
        /// <param name="scene">The scene to render</param>
        public void Render(IRendererTarget target, Scene scene, RenderParameters parameters)
        {
            _scene = scene;

            if (_parameters != null && _parameters.Equals(parameters))
                ResetOGL();
            else
                Reset();

            _parameters = ((GLRenderParameters)parameters).Clone();

            _GLModeSettings = new GLModeSettings();

            //---- Share contexts
            GlContextManager.ShareContext(Wgl.wglGetCurrentContext());

            //---- Initialize
            Initialize();

            if (_parameters.RenderingStyle == GLRenderParameters.RenderStyle.WireframeOnShaded)
            {
                Gl.glEnable(Gl.GL_POLYGON_OFFSET_FILL);
                Gl.glPolygonOffset(1f, 1f);
            }

            //---- Render the scene
            for (int index = 0; index < scene.Layers.Count; index++)
            {
                Layer layer = scene.Layers[index];
                if (layer.IsVisible)
                    Render_Layer(layer);
            }

            //---- Pass 2 : Wireframe On Shaded
            if (_parameters.RenderingStyle == GLRenderParameters.RenderStyle.WireframeOnShaded)
            {
                Gl.glDisable(Gl.GL_POLYGON_OFFSET_FILL);

                _parameters.RenderingStyle = GLRenderParameters.RenderStyle.Wireframe;

                ResetOGL();
                Initialize();

                for (int index = 0; index < scene.Layers.Count; index++)
                {
                    Layer layer = scene.Layers[index];
                    if (layer.IsVisible)
                        Render_Layer(layer);
                }

                _parameters.RenderingStyle = GLRenderParameters.RenderStyle.WireframeOnShaded;
            }

            ResetOGL();

            _refreshModel = false;
        }

        #endregion

        #region Initialize

        private void Initialize()
        {
            //---- Extensions support ?
            _isVBOSupported = Gl.IsExtensionSupported("GL_ARB_vertex_buffer_object");

            //---- Reset some parameters
            Gl.glShadeModel(Gl.GL_SMOOTH);
            Gl.glLineWidth(1);
            for (int index = 0; index < 8; index++)
            {
                ResetLight(Gl.GL_LIGHT0 + index); // Usefull ?
                Gl.glDisable(Gl.GL_LIGHT0 + index);
            }
            _lightId = Gl.GL_LIGHT0;

            //---- Individual lighting
            _GLModeSettings.GL_LIGHTING = (_parameters.RenderingStyle != GLRenderParameters.RenderStyle.Wireframe);

            if (_GLModeSettings.GL_LIGHTING)
                Gl.glEnable(Gl.GL_LIGHTING);
            else
                Gl.glDisable(Gl.GL_LIGHTING);

            //---- Lightning
            if (_parameters.ShowLightning)
                ShowLight();
            else
                HideLight();

            //---- Color material ?
            _GLModeSettings.GL_COLOR_MATERIAL = !_parameters.ShowLightning && !_parameters.ShowTextures;
            if (_GLModeSettings.GL_COLOR_MATERIAL)
                Gl.glEnable(Gl.GL_COLOR_MATERIAL);
            else
                Gl.glDisable(Gl.GL_COLOR_MATERIAL);

            //---- Textures
            _GLModeSettings.GL_TEXTURE = _parameters.ShowTextures;
            _GLModeSettings.GL_TEXTURE &= (_parameters.RenderingStyle == GLRenderParameters.RenderStyle.Shaded ||
                                           _parameters.RenderingStyle == GLRenderParameters.RenderStyle.WireframeOnShaded);

            //---- Fill or line
            if (_parameters.RenderingStyle == GLRenderParameters.RenderStyle.Wireframe)
                Gl.glPolygonMode(Gl.GL_FRONT_AND_BACK, Gl.GL_LINE);
            else
                Gl.glPolygonMode(Gl.GL_FRONT_AND_BACK, Gl.GL_FILL);
        }

        #endregion

        #region ShowLight

        // http://jerome.jouvie.free.fr/OpenGl/Tutorials/Tutorial13.php
        // http://jerome.jouvie.free.fr/OpenGl/Lessons/Lesson6.php
        private void ShowLight()
        {
            //---- Global illumination
            //float[] al = new float[] { 0f, 0f, 0f, 1.0f };
            //Gl.glLightModelfv(Gl.GL_LIGHT_MODEL_AMBIENT, al);
            //Gl.glLightModeli(Gl.GL_LIGHT_MODEL_LOCAL_VIEWER, Gl.GL_TRUE);

            List<LightSource> lights = HierarchyHelper.FindAll<LightSource>(_scene, RSType.LightSource);
            for (int index = 0; index < lights.Count; index++)
                if (lights[index].IsVisible)
                    InitializeLight(lights[index]);
        }

        #endregion

        #region HideLight

        private void HideLight()
        {
            //---- Global illumination
            //float[] al = new float[] { 0.1f, 0.1f, 0.1f, 1.0f };
            //Gl.glLightModelfv(Gl.GL_LIGHT_MODEL_AMBIENT, al);
            //Gl.glLightModeli(Gl.GL_LIGHT_MODEL_LOCAL_VIEWER, Gl.GL_TRUE);

            //---- Enable color material
            //Gl.glEnable(Gl.GL_COLOR_MATERIAL);
            //_GLModeSettings.GL_COLOR_MATERIAL = true;

            //---- Default lights
            float[] LightAmbient = new float[] { 0f, 0f, 0f, 1.0f };
            float[] LightDiffuse = new float[] { 0.2f, 0.2f, 0.2f, 1.0f };
            float[] LightSpecular = new float[] { 0.2f, 0.2f, 0.2f, 1.0f };
            float[] LightPosition;

            float lightDistance = 100000;

            LightPosition = new float[] { .0f, .0f, lightDistance, 1.0f };
            Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_AMBIENT, LightAmbient);
            Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_DIFFUSE, LightDiffuse);
            Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_SPECULAR, LightSpecular);
            Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_POSITION, LightPosition);
            Gl.glEnable(Gl.GL_LIGHT0);

            LightPosition = new float[] { lightDistance, .0f, .0f, 1.0f };
            Gl.glLightfv(Gl.GL_LIGHT1, Gl.GL_AMBIENT, LightAmbient);
            Gl.glLightfv(Gl.GL_LIGHT1, Gl.GL_DIFFUSE, LightDiffuse);
            Gl.glLightfv(Gl.GL_LIGHT1, Gl.GL_SPECULAR, LightSpecular);
            Gl.glLightfv(Gl.GL_LIGHT1, Gl.GL_POSITION, LightPosition);
            Gl.glEnable(Gl.GL_LIGHT1);

            LightPosition = new float[] { .0f, .0f, -lightDistance, 1.0f };
            Gl.glLightfv(Gl.GL_LIGHT2, Gl.GL_AMBIENT, LightAmbient);
            Gl.glLightfv(Gl.GL_LIGHT2, Gl.GL_DIFFUSE, LightDiffuse);
            Gl.glLightfv(Gl.GL_LIGHT2, Gl.GL_SPECULAR, LightSpecular);
            Gl.glLightfv(Gl.GL_LIGHT2, Gl.GL_POSITION, LightPosition);
            Gl.glEnable(Gl.GL_LIGHT2);

            LightPosition = new float[] { -lightDistance, .0f, .0f, 1.0f };
            Gl.glLightfv(Gl.GL_LIGHT3, Gl.GL_AMBIENT, LightAmbient);
            Gl.glLightfv(Gl.GL_LIGHT3, Gl.GL_DIFFUSE, LightDiffuse);
            Gl.glLightfv(Gl.GL_LIGHT3, Gl.GL_SPECULAR, LightSpecular);
            Gl.glLightfv(Gl.GL_LIGHT3, Gl.GL_POSITION, LightPosition);
            Gl.glEnable(Gl.GL_LIGHT3);

            LightPosition = new float[] { .0f, lightDistance, .0f, 1.0f };
            Gl.glLightfv(Gl.GL_LIGHT4, Gl.GL_AMBIENT, LightAmbient);
            Gl.glLightfv(Gl.GL_LIGHT4, Gl.GL_DIFFUSE, LightDiffuse);
            Gl.glLightfv(Gl.GL_LIGHT4, Gl.GL_SPECULAR, LightSpecular);
            Gl.glLightfv(Gl.GL_LIGHT4, Gl.GL_POSITION, LightPosition);
            Gl.glEnable(Gl.GL_LIGHT4);

            LightPosition = new float[] { .0f, -lightDistance, .0f, 1.0f };
            Gl.glLightfv(Gl.GL_LIGHT5, Gl.GL_AMBIENT, LightAmbient);
            Gl.glLightfv(Gl.GL_LIGHT5, Gl.GL_DIFFUSE, LightDiffuse);
            Gl.glLightfv(Gl.GL_LIGHT5, Gl.GL_SPECULAR, LightSpecular);
            Gl.glLightfv(Gl.GL_LIGHT5, Gl.GL_POSITION, LightPosition);
            Gl.glEnable(Gl.GL_LIGHT5);
        }

        #endregion

        #region ResetOGL

        public void ResetOGL()
        {
            Gl.glCullFace(Gl.GL_BACK);
            Gl.glFrontFace(Gl.GL_CCW);

            // enable scissor testing (for viewports)
            //Gl.glEnable(Gl.GL_SCISSOR_TEST);
            // set the scissor area for the viewport
            //Gl.glScissor(x, y, width, height);

            try
            {
                Gl.glPolygonMode(Gl.GL_FRONT_AND_BACK, Gl.GL_FILL);

                Gl.glDisable(Gl.GL_LIGHTING);
                Gl.glDisable(Gl.GL_COLOR_MATERIAL);
                Gl.glDisable(Gl.GL_LIGHT0);
                Gl.glDisable(Gl.GL_LIGHT1);
                Gl.glDisable(Gl.GL_LIGHT2);
                Gl.glDisable(Gl.GL_LIGHT3);
                Gl.glDisable(Gl.GL_LIGHT4);
                Gl.glDisable(Gl.GL_LIGHT5);
                Gl.glDisable(Gl.GL_LIGHT6);
                Gl.glDisable(Gl.GL_LIGHT7);

                //---- Global illumination
                //float[] al = new float[] { 0f, 0f, 0f, 1.0f };
                //Gl.glLightModelfv(Gl.GL_LIGHT_MODEL_AMBIENT, al);
                //Gl.glLightModeli(Gl.GL_LIGHT_MODEL_LOCAL_VIEWER, Gl.GL_TRUE);
            }
            catch (Exception e)
            {

            }
        }

        #endregion

        #region Reset

        public void Reset()
        {
            ResetOGL();

            _refreshModel = true;

            //_normalDLM.Reset();
        }

        #endregion

        #region Render_Layer

        private void Render_Layer(Layer layer)
        {
            for (int index = 0; index < layer.Nodes.Count; index++)
            {
                Node node = layer.Nodes[index];
                if (!node.IsVisible)
                    continue;

                Render_Node(node);
            }

            for (int index = 0; index < layer.Geometries.Count; index++)
            {
                Geometry geometry = layer.Geometries[index];
                if (!geometry.IsVisible)
                    continue;

                Render_Geometry(geometry);
            }

            for (int index = 0; index < layer.Lights.Count; index++)
            {
                LightSource light = layer.Lights[index];
                if (!light.IsVisible)
                    continue;

                Render_Light(light);
            }
        }

        #endregion

        #region Render_Node

        private void Render_Node(Node node)
        {
            if (node.Type == RSType.NodeRef)
                PushMatrix(node.WorldTransform.Values);

            for (int index = 0; index < node.Children.Count; index++)
            {
                Node child = node.Children[index];
                if (!child.IsVisible)
                    continue;

                Render_Node(child);
            }

            for (int index = 0; index < node.Geometries.Count; index++)
            {
                Geometry geometry = node.Geometries[index];
                if (!geometry.IsVisible)
                    continue;

                Render_Geometry(geometry);
            }

            for (int index = 0; index < node.Lights.Count; index++)
            {
                LightSource light = node.Lights[index];
                if (!light.IsVisible)
                    continue;

                Render_Light(light);
            }

            if (node.Type == RSType.NodeRef)
                PopMatrix();
        }

        #endregion

        #region Render_Geometry

        private void Render_Geometry(Geometry geometry)
        {
            //---- Sub geometries
            for (int index = 0; index < geometry.Children.Count; index++)
                if (geometry.Children[index].IsVisible)
                    Render_Geometry(geometry.Children[index]);

            //---- Check for the selection
            if (_parameters.SelectionMode == SelectionMode.Geometry &&
                IsSelected(RSType.Geometry, geometry))
            {
                PushSelectionColor();
            }
            else
            {
                //---- Set up the material
                if (_parameters.RenderingStyle == GLRenderParameters.RenderStyle.Wireframe)
                    Gl.glColor3fv(COLOR_WIREFRAME);
                else if (_parameters.ShowLightning || _parameters.ShowTextures)
                {
                    Material material = geometry.Material;
                    if (material == null)
                        material = Material.DefaultMaterial;

                    // Color shader
                    if (material.Ambient is ColorMap)
                        Gl.glMaterialfv(Gl.GL_FRONT_AND_BACK, Gl.GL_AMBIENT, material.Ambient.DefaultColor);
                    if (material.Diffuse is ColorMap)
                        Gl.glMaterialfv(Gl.GL_FRONT_AND_BACK, Gl.GL_DIFFUSE, material.Diffuse.DefaultColor);
                    if (material.Specular is ColorMap)
                        Gl.glMaterialfv(Gl.GL_FRONT_AND_BACK, Gl.GL_SPECULAR, material.Specular.DefaultColor);
                    if (material.Emission is ColorMap)
                        Gl.glMaterialfv(Gl.GL_FRONT_AND_BACK, Gl.GL_EMISSION, material.Emission.DefaultColor);
                    Gl.glMaterialf(Gl.GL_FRONT_AND_BACK, Gl.GL_SHININESS, material.Shininess);
                }
                else
                    Gl.glColor3fv(COLOR_SHADEDMODE);
            }

            //---- Textures
            if (geometry.Material != null && _GLModeSettings.GL_TEXTURE)
            {
                //Gl.glLightModeli(Gl.GL_LIGHT_MODEL_COLOR_CONTROL, Gl.GL_SEPARATE_SPECULAR_COLOR);

                //  ARB_multitexture

                TextureColorMap texture = geometry.Material.Diffuse as TextureColorMap;
                if (texture != null)
                    BindTexture(texture);

                texture = geometry.Material.Specular as TextureColorMap;
                if (texture != null)
                    BindTexture(texture);

                texture = geometry.Material.Ambient as TextureColorMap;
                if (texture != null)
                    BindTexture(texture);
            }

            //---- Apply the transformations
            LoadMatrix(geometry.WorldTransform.Values);

            //---- Render the geometry
            RenderSpecificGeometry(geometry);

            //---- Pop the selection color
            PopSelectionColor();

            //---- Draw Normals
            if (_parameters.ShowNormals)
                Render_Normals(geometry);
        }

        #endregion

        #region RenderSpecificGeometry

        private void RenderSpecificGeometry(Geometry geometry)
        {
            MeshGeometry mesh = null;
            if (geometry.Type == RSType.MeshGeometry)
                mesh = (MeshGeometry)geometry;

            if (mesh != null && mesh.Indices.Count > 0)
            {
                if (_parameters.SelectionMode == SelectionMode.Face && IsSelected(RSType.Geometry, geometry))
                    RenderMeshGeometry_Faces(mesh);
                else
                    RenderMeshGeometry_VBO(mesh);
            }
        }

        #endregion

        #region RenderMeshGeometry_VBO

        private void RenderMeshGeometry_VBO(MeshGeometry polygon)
        {
            //---- Get the GL-drawing/context information for this polygon
            bool uploadData = false;
            GlDrawingAI glDrawing = polygon.FindAssociatedInfo(GlDrawingAI.INFOTYPEID) as GlDrawingAI;
            if (glDrawing == null)
            {
                glDrawing = new GlDrawingAI();
                polygon.Add(glDrawing);
                uploadData = true;
            }

            //---- Enable 
            Gl.glEnableClientState(Gl.GL_NORMAL_ARRAY);
            Gl.glEnableClientState(Gl.GL_VERTEX_ARRAY);
            Gl.glEnableClientState(Gl.GL_TEXTURE_COORD_ARRAY);

            // If the list doesn' exist -> create it, bind it, upload data
            // Otherwise, bind it and upload data
            // if (Gl.glIsBufferARB(glDrawing.vertexVBO) == 0)

            if (_isVBOSupported)
            {

                #region VBO

                // Create the VBO and upload data.
                if (_refreshModel || uploadData)
                {
                    //---- VBO for vertices
                    Gl.glGenBuffersARB(1, out glDrawing.geometryVertexVBO);
                    Gl.glBindBufferARB(Gl.GL_ARRAY_BUFFER_ARB, glDrawing.geometryVertexVBO);

                    // Allocate memory (NULL, which means we want GL to allocate memory but not initialize it.)
                    int sizeInBytes = polygon.Vertices.Count * Vertex.SIZEOF;
                    Gl.glBufferDataARB(Gl.GL_ARRAY_BUFFER_ARB, (IntPtr)sizeInBytes, IntPtr.Zero, Gl.GL_STATIC_DRAW_ARB);

                    // Upload the vertices
                    GCHandle pointer = GCHandle.Alloc(polygon.Vertices.Items, GCHandleType.Pinned);
                    Gl.glBufferSubDataARB(Gl.GL_ARRAY_BUFFER_ARB, IntPtr.Zero, (IntPtr)sizeInBytes, (IntPtr)pointer.AddrOfPinnedObject());
                    pointer.Free();

                    //---- Make a VBO for indices
                    Gl.glGenBuffers(1, out glDrawing.geometryIndiceVBO);
                    Gl.glBindBuffer(Gl.GL_ELEMENT_ARRAY_BUFFER, glDrawing.geometryIndiceVBO);
                    sizeInBytes = polygon.Indices.Count * sizeof(int);
                    Gl.glBufferData(Gl.GL_ELEMENT_ARRAY_BUFFER, (IntPtr)sizeInBytes, null, Gl.GL_STATIC_DRAW);

                    pointer = GCHandle.Alloc(polygon.Indices.Items, GCHandleType.Pinned);
                    Gl.glBufferSubDataARB(Gl.GL_ELEMENT_ARRAY_BUFFER, IntPtr.Zero, (IntPtr)sizeInBytes, (IntPtr)pointer.AddrOfPinnedObject());
                    pointer.Free();
                }
                else
                {
                    Gl.glBindBufferARB(Gl.GL_ARRAY_BUFFER_ARB, glDrawing.geometryVertexVBO);
                    Gl.glBindBuffer(Gl.GL_ELEMENT_ARRAY_BUFFER, glDrawing.geometryIndiceVBO);
                }

                // The pointers are relative to the zero point of the VBO. 
                Gl.glVertexPointer(3, Gl.GL_FLOAT, Vertex.SIZEOF, IntPtr.Zero);
                Gl.glNormalPointer(Gl.GL_FLOAT, Vertex.SIZEOF, (IntPtr)(3 * sizeof(float)));
                Gl.glTexCoordPointer(2, Gl.GL_FLOAT, Vertex.SIZEOF, (IntPtr)(6 * sizeof(float)));

                //glColorPointer(GLint size, GLenum type, GLsizei stride, GLvoid* pointer);
                //glEdgeFlagPointer(GLsizei stride, GLvoid* pointer);
                //glFogCoordPointer(GLenum type, GLsizei stride, GLvoid* pointer);
                //Gl.glClientActiveTexture(Gl.GL_TEXTURE2);                

                #endregion

            }
            else
            {

                #region No VBO support : use the CPU memory

                Gl.glVertexPointer(3, Gl.GL_FLOAT, Vertex.SIZEOF, polygon.Vertices.Items);
                Gl.glNormalPointer(Gl.GL_FLOAT, Vertex.SIZEOF, polygon.Vertices.Items[0].Normal);
                Gl.glTexCoordPointer(2, Gl.GL_FLOAT, Vertex.SIZEOF, polygon.Vertices.Items[0].TexCoordinate);

                #endregion

            }

            //---- Draw the polygons
            if (!polygon.IsUniform)
            {
                int offset = 0;
                for (int index = 0; index < polygon.FacesCount; index++)
                {
                    //IntPtr offsetPtr = new IntPtr(offset * sizeof(int));
                    //Gl.glDrawElements(Gl.GL_POLYGON, polygon.VerticesPerFace(index), Gl.GL_UNSIGNED_INT, offsetPtr);
                    Gl.glDrawArrays(Gl.GL_POLYGON, offset, polygon.VerticesPerFace(index));
                    offset += polygon.VerticesPerFace(index);
                }
            }
            else if (polygon.VerticesPerFace(0) == 3)
                Gl.glDrawElements(Gl.GL_TRIANGLES, polygon.Indices.Count, Gl.GL_UNSIGNED_INT, IntPtr.Zero);
            //Gl.glDrawArrays(Gl.GL_TRIANGLES, 0, polygon.Indices.Count);
            else if (polygon.VerticesPerFace(0) == 4)
                Gl.glDrawElements(Gl.GL_QUADS, polygon.Indices.Count, Gl.GL_UNSIGNED_INT, IntPtr.Zero);
            //Gl.glDrawArrays(Gl.GL_QUADS, 0, polygon.Indices.Count);

            //---- Draw the edges
            //    Gl.glDisable(Gl.GL_LIGHTING);
            //    Gl.glColor4f(0.2f, 0.2f, 0.2f, 0.2f);
            //    Gl.glEnable(Gl.GL_BLEND);
            //    Gl.glBindBufferARB(Gl.GL_ARRAY_BUFFER_ARB, edgeVboName);
            //    Gl.glVertexPointer(3, Gl.GL_FLOAT, 0, IntPtr.Zero);
            //    Gl.glDrawArrays(Gl.GL_LINES, 0, mesh.Edges.Count * 2);
            //    Gl.glDisable(Gl.GL_BLEND);

            //----
            Gl.glDisableClientState(Gl.GL_TEXTURE_COORD_ARRAY);
            Gl.glDisableClientState(Gl.GL_VERTEX_ARRAY);
            Gl.glDisableClientState(Gl.GL_NORMAL_ARRAY);
        }

        #endregion

        #region RenderMeshGeometry_Faces

        private void RenderMeshGeometry_Faces(MeshGeometry polygon)
        {
            bool isUniform = polygon.IsUniform;

            if (isUniform)
            {
                if (polygon.VerticesPerFace(0) == 3)
                    Gl.glBegin(Gl.GL_TRIANGLES);
                else if (polygon.VerticesPerFace(0) == 4)
                    Gl.glBegin(Gl.GL_QUADS);
            }

            int indiceIndex = 0;
            for (int index0 = 0; index0 < polygon.FacesCount; index0++)
            {
                int count = polygon.VerticesPerFace(index0);

                //---- Face selection
                if (_parameters.SelectionMode == SelectionMode.Face &&
                    IsSelected(RSType.Face, index0))
                    PushSelectionColor();

                //---- Drawing
                if (!isUniform)
                    Gl.glBegin(Gl.GL_POLYGON);
                for (int index = 0; index < count; index++)
                {
                    int indice = polygon.Indices[indiceIndex];
                    Vertex vertex = polygon.Vertices[indice];

                    //if (normal.Length < 0.1)
                    //    break;

                    if (_GLModeSettings.GL_TEXTURE)
                        Gl.glTexCoord2fv(ref vertex.TexCoordinate.x);

                    if (_GLModeSettings.GL_LIGHTING)
                        Gl.glNormal3fv(ref vertex.Normal.x);

                    Gl.glVertex3fv(ref vertex.Coordinate.x);

                    indiceIndex++;
                }
                if (!isUniform)
                    Gl.glEnd();

                PopSelectionColor();
            }

            if (isUniform)
                Gl.glEnd();
        }

        #endregion

        #region Render_Light

        private void Render_Light(LightSource light)
        {
            if (light is AmbientLight)
                Render_AmbientLight((AmbientLight)light);
            else if (light is DirectionalLight)
                Render_DirectionalLight((DirectionalLight)light);
        }

        private void Render_AmbientLight(AmbientLight light)
        {
            //--- Axis
            Gl.glColor3fv(COLOR_LIGHT);

            DrawCircle(light.Position, 0.2f);
            //Gl.glVertex3dv(light.Position);
            //Gl.glVertex3dv(light.ApplyTransformation(Vector3.UnitX));
        }

        private void Render_DirectionalLight(DirectionalLight light)
        {
            //--- Axis
            Gl.glColor3fv(COLOR_LIGHT);

            LoadMatrix(light.WorldTransform.Values);

            DrawCircle(light.Position, 0.01f);
            /*
                        // X
                        Gl.glBegin(Gl.GL_LINE);
                        Gl.glColor3fv(COLOR_AXIS_X);
                        Gl.glVertex3dv(light.Position);
                        Gl.glVertex3dv(Vector3.UnitX);
                        Gl.glEnd();

                        // Y
                        Gl.glBegin(Gl.GL_LINE);
                        Gl.glColor3fv(COLOR_AXIS_Y);
                        Gl.glVertex3dv(light.Position);
                        Gl.glVertex3dv(Vector3.UnitY);
                        Gl.glEnd();

                        // Z
                        Gl.glBegin(Gl.GL_LINE);
                        Gl.glColor3fv(COLOR_AXIS_Z);
                        Gl.glVertex3dv(light.Position);
                        Gl.glVertex3dv(Vector3.UnitZ);
                        Gl.glEnd();*/
        }

        #endregion

        #region Render_Normals

        private void Render_Normals(Geometry geometry)
        {
            GlDrawingAI glDrawing = geometry.FindAssociatedInfo(GlDrawingAI.INFOTYPEID) as GlDrawingAI;

            // Can occur, with empty geometry
            if (glDrawing == null)
                return;

            try
            {
                Gl.glDisable(Gl.GL_TEXTURE_1D);
                Gl.glDisable(Gl.GL_TEXTURE_2D);
                Gl.glDisable(Gl.GL_TEXTURE_3D);
                Gl.glDisable(Gl.GL_LIGHTING);
                Gl.glEnable(Gl.GL_COLOR_MATERIAL);
                Gl.glColor3f(COLOR_NORMAL[0], COLOR_NORMAL[1], COLOR_NORMAL[2]);

                Render_MeshGeometryNormals((MeshGeometry)geometry, glDrawing);
            }
            finally
            {
                if (_GLModeSettings.GL_LIGHTING)
                    Gl.glEnable(Gl.GL_LIGHTING);

                if (!_GLModeSettings.GL_COLOR_MATERIAL)
                    Gl.glDisable(Gl.GL_COLOR_MATERIAL);
            }
        }

        #endregion

        #region Render_MeshGeometryNormals

        private void Render_MeshGeometryNormals(MeshGeometry mesh, GlDrawingAI glDrawing)
        {
            //---- Create the normals
            Vector3[] normals = null;

            if (!_isVBOSupported || glDrawing.normalVBO == 0)
            {
                normals = new Vector3[mesh.Vertices.Count * 2];
                for (int index = 0; index < mesh.Vertices.Count; index++)
                {
                    normals[index * 2] = mesh.Vertices[index].Coordinate;
                    normals[index * 2 + 1] = mesh.Vertices[index].Coordinate + mesh.Vertices[index].Normal;
                }
            }

            Gl.glEnableClientState(Gl.GL_VERTEX_ARRAY);

            if (_isVBOSupported)
            {

                #region VBO

                // Create the VBO and upload data.
                if (glDrawing.normalVBO == 0)
                {
                    //---- VBO for vertices
                    Gl.glGenBuffersARB(1, out glDrawing.normalVBO);
                    Gl.glBindBufferARB(Gl.GL_ARRAY_BUFFER_ARB, glDrawing.normalVBO);

                    // Allocate memory (NULL, which means we want GL to allocate memory but not initialize it.)
                    int sizeInBytes = mesh.Vertices.Count * 2 * sizeof(Vector3);
                    Gl.glBufferDataARB(Gl.GL_ARRAY_BUFFER_ARB, (IntPtr)sizeInBytes, IntPtr.Zero, Gl.GL_STATIC_DRAW_ARB);

                    // Upload the normals
                    GCHandle pointer = GCHandle.Alloc(normals, GCHandleType.Pinned);
                    Gl.glBufferSubDataARB(Gl.GL_ARRAY_BUFFER_ARB, IntPtr.Zero, (IntPtr)sizeInBytes, (IntPtr)pointer.AddrOfPinnedObject());
                    pointer.Free();
                }
                else
                {
                    Gl.glBindBufferARB(Gl.GL_ARRAY_BUFFER_ARB, glDrawing.normalVBO);
                }

                Gl.glVertexPointer(3, Gl.GL_FLOAT, sizeof(Vector3), IntPtr.Zero);

                #endregion

            }
            else
            {

                #region No VBO support : use the CPU memory

                Gl.glVertexPointer(3, Gl.GL_FLOAT, sizeof(Vector3), normals);

                #endregion

            }

            //---- Draw the normals
            Gl.glDrawArrays(Gl.GL_LINES, 0, mesh.Vertices.Count * 2);

            Gl.glDisableClientState(Gl.GL_VERTEX_ARRAY);
        }

        #endregion

        #region InitializeLight

        private void InitializeLight(LightSource source)
        {
            if (_lightId > Gl.GL_LIGHT7)
                return;

            //---- Apply the transformations
            LoadMatrix(source.WorldTransform.Values);

            //---- Set up the light
            Gl.glEnable(_lightId);

            if (source is DirectionalLight)
                InitializeLight((DirectionalLight)source, _lightId);
            else if (source is PositionalLight)
                InitializeLight((PositionalLight)source, _lightId);
            else if (source is SpotLight)
                InitializeLight((SpotLight)source, _lightId);
            else if (source is AmbientLight)
                InitializeLight((AmbientLight)source, _lightId);

            _lightId++;
        }

        private void InitializeLight(AmbientLight light, int lightId)
        {
            // Ambient light source (w = 0)
            // The light source is at an infinite distance,
            // all the ray are parallel and have the direction (x, y, z).
            float[] position = new float[] { (float)light.Position.x, (float)light.Position.y, (float)light.Position.z, 0f };

            Gl.glLightfv(lightId, Gl.GL_AMBIENT, light.Ambient);
            Gl.glLightfv(lightId, Gl.GL_DIFFUSE, light.Diffuse);
            Gl.glLightfv(lightId, Gl.GL_SPECULAR, light.Specular);
            Gl.glLightfv(lightId, Gl.GL_POSITION, position);
        }

        private void InitializeLight(DirectionalLight light, int lightId)
        {
            // Directional light source (w = 0)
            // The light source is at an infinite distance,
            // all the ray are parallel and have the direction (x, y, z).
            float[] position = new float[] { (float)light.Position.x, (float)light.Position.y, (float)light.Position.z, 0f };

            Gl.glLightfv(lightId, Gl.GL_AMBIENT, light.Ambient);
            Gl.glLightfv(lightId, Gl.GL_DIFFUSE, light.Diffuse);
            Gl.glLightfv(lightId, Gl.GL_SPECULAR, light.Specular);
            Gl.glLightfv(lightId, Gl.GL_POSITION, position);
        }

        private void InitializeLight(PositionalLight light, int lightId)
        {
            // Positional light source (w = 1)
            // The light source is positioned at (x, y, z).
            // The ray come from this particular location (x, y, z) and goes towards all directions.
            float[] position = new float[] { (float)light.Position.x, (float)light.Position.y, (float)light.Position.z, 1f };

            Gl.glLightfv(lightId, Gl.GL_AMBIENT, light.Ambient);
            Gl.glLightfv(lightId, Gl.GL_DIFFUSE, light.Diffuse);
            Gl.glLightfv(lightId, Gl.GL_SPECULAR, light.Specular);
            Gl.glLightfv(lightId, Gl.GL_POSITION, position);
        }

        private void InitializeLight(SpotLight light, int lightId)
        {
            // This light is a spot. A spot emits lights on a particular direction.
            // To create a spot, we have to specify the spot direction (GL_SPOT_DIRECTION)
            // and the spot cut-off (GL_SPOT_CUTOFF).
            // The spot cut-off is the angle of the cone in which the spot emits light. The
            // axis of the cone is the spo direction.
            float[] position = new float[] { (float)light.Position.x, (float)light.Position.y, (float)light.Position.z, 1f };

            Gl.glLightfv(lightId, Gl.GL_AMBIENT, light.Ambient);
            Gl.glLightfv(lightId, Gl.GL_DIFFUSE, light.Diffuse);
            Gl.glLightfv(lightId, Gl.GL_SPECULAR, light.Specular);
            Gl.glLightfv(lightId, Gl.GL_POSITION, position);

            // Update position, direction and cut-off of the light
            float[] direction = new float[] { (float)light.Direction.x, (float)light.Direction.y, (float)light.Direction.z };
            Gl.glLightfv(lightId, Gl.GL_SPOT_DIRECTION, direction);

            // Angle of the cone light emitted by the spot : value between 0 to 180
            Gl.glLightf(lightId, Gl.GL_SPOT_CUTOFF, light.ConeAngle);

            // Exponent propertie defines the concentration of the light
            Gl.glLightf(lightId, Gl.GL_SPOT_EXPONENT, light.LightConcentration);

            // Light attenuation
            Gl.glLightf(lightId, Gl.GL_CONSTANT_ATTENUATION, light.ConstantAttenuation);
            Gl.glLightf(lightId, Gl.GL_LINEAR_ATTENUATION, light.LinearAttenuation);
            Gl.glLightf(lightId, Gl.GL_QUADRATIC_ATTENUATION, light.QuadraticAttenuation);
        }

        #endregion

        #region Helpers : IsSelected

        private bool IsSelected(RSType type, object data)
        {
            if (_parameters.Selection == null ||
                _parameters.Selection.Count < 1)
                return false;


            #region Geometry

            if (_parameters.SelectionMode == SelectionMode.Geometry)
            {
                if (_parameters.Selection.Contains(data))
                    return true;
            }

            #endregion

            #region Face

            if (_parameters.SelectionMode == SelectionMode.Face)
            {
                // Does a face is selected
                if (type == RSType.Face)
                {
                    int faceIndex = (int)data;
                    for (int index = 0; index < _parameters.Selection.Count; index++)
                    {
                        FaceProxy face = (FaceProxy)_parameters.Selection[index];
                        /*face.Geometry.Id == polygon.Id && */
                        if (face.FaceIndex == faceIndex)
                            return true;
                    }
                }

                // Does this geometry is selected
                if (type == RSType.Geometry)
                {
                    Geometry geometry = (Geometry)data;
                    for (int index = 0; index < _parameters.Selection.Count; index++)
                        if (((FaceProxy)_parameters.Selection[index]).Geometry.Id == geometry.Id)
                            return true;
                }
            }

            #endregion

            return false;
        }

        #endregion

        #region Helpers : DrawCircle

        private void DrawCircle(Vector3 position, float radius)
        {
            Gl.glBegin(Gl.GL_LINE_LOOP);

            for (float angle = 0.0f; angle <= (2.0f * 3.14159); angle += 0.01f)
            {
                float x = position.x + (radius * (float)System.Math.Sin(angle));
                float y = position.y + (radius * (float)System.Math.Cos(angle));
                Gl.glVertex3d(x, y, position.z);
            }

            Gl.glEnd();
        }

        #endregion

        #region Helpers : BindTexture

        private void BindTexture(TextureColorMap texture)
        {
            // Insure that the texture is loaded
            texture.Load();

            //---- Use OpenGL to generate a new texture and bind it to the 2D textured target.
            uint[] textureId = new uint[1];
            Gl.glEnable(Gl.GL_TEXTURE_2D);

            Gl.glBindTexture(Gl.GL_TEXTURE_2D, texture.Image.GLImageId);

            //Gl.glEnable(Gl.GL_COLOR_MATERIAL);
            //Gl.glColor4f(1f, 1f, 1f, 1f);

            //---- Filtering
            if (texture.UseMipMap)
            {
                switch (texture.TextureMagnification)
                {
                    case TextureMagnification.Linear:
                        Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR_MIPMAP_LINEAR);
                        break;
                    case TextureMagnification.Nearest:
                        Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_NEAREST_MIPMAP_NEAREST);
                        break;
                    //case TextureMagnification.Anisotropic:
                    //    Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, ??);
                    //    break;
                }
            }
            else
            {
                switch (texture.TextureMagnification)
                {
                    case TextureMagnification.Linear:
                        Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR);
                        break;
                    case TextureMagnification.Nearest:
                        Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_NEAREST);
                        break;
                    //case TextureMagnification.Anisotropic:
                    //    Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, ??);
                    //    break;
                }
            }

            if (texture.UseMipMap)
            {
                switch (texture.TextureMinimization)
                {
                    case TextureMinimization.Linear:
                        Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_LINEAR_MIPMAP_LINEAR);
                        break;
                    case TextureMinimization.Nearest:
                        Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_NEAREST_MIPMAP_NEAREST);
                        break;
                    //case TextureMinimization.Anisotropic:
                    //    Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, ??);
                    //    break;
                }
            }
            else
            {
                switch (texture.TextureMinimization)
                {
                    case TextureMinimization.Linear:
                        Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_LINEAR);
                        break;
                    case TextureMinimization.Nearest:
                        Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_NEAREST);
                        break;
                    //case TextureMinimization.Anisotropic:
                    //    Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, ??);
                    //    break;
                }
            }

            //---- Wrap
            switch (texture.WrapS)
            {
                //case TextureWrap.Clamp:
                //    Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_CLAMP);
                //    break;
                case TextureWrap.Wrap:
                    Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_REPEAT);
                    break;
                case TextureWrap.Mirror:
                    Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_MIRRORED_REPEAT);
                    break;
                case TextureWrap.Clamp:
                    Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_CLAMP_TO_EDGE);
                    break;
                case TextureWrap.Border:
                    Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_CLAMP_TO_BORDER);
                    break;
            }

            switch (texture.WrapT)
            {
                //case TextureWrap.Clamp:
                //    Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_CLAMP);
                //    break;
                case TextureWrap.Wrap:
                    Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_REPEAT);
                    break;
                case TextureWrap.Mirror:
                    Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_MIRRORED_REPEAT);
                    break;
                case TextureWrap.Clamp:
                    Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_CLAMP_TO_EDGE);
                    break;
                case TextureWrap.Border:
                    Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_CLAMP_TO_BORDER);
                    break;
            }

            //---- We modulate with the current material
            Gl.glTexEnvi(Gl.GL_TEXTURE_ENV, Gl.GL_TEXTURE_ENV_MODE, (int)Gl.GL_MODULATE);
        }

        #endregion

        #region Helper : PushSelectionColor / PopSelectionColor

        public void PushSelectionColor()
        {
            int colorMaterial;
            Gl.glGetBooleanv(Gl.GL_COLOR_MATERIAL, out colorMaterial);

            _selectionColorStack = new GLShadingItem((colorMaterial > 0));

            if (_selectionColorStack.IsColorMaterial)
                Gl.glGetFloatv(Gl.GL_CURRENT_COLOR, _selectionColorStack.Color);
            else
            {
                Gl.glGetMaterialfv(Gl.GL_FRONT_AND_BACK, Gl.GL_AMBIENT, _selectionColorStack.Ambient);
                Gl.glGetMaterialfv(Gl.GL_FRONT_AND_BACK, Gl.GL_DIFFUSE, _selectionColorStack.Diffuse);
                Gl.glGetMaterialfv(Gl.GL_FRONT_AND_BACK, Gl.GL_SPECULAR, _selectionColorStack.Specular);
                Gl.glGetMaterialfv(Gl.GL_FRONT_AND_BACK, Gl.GL_EMISSION, _selectionColorStack.Emissive);
            }

            Gl.glEnable(Gl.GL_COLOR_MATERIAL);
            Gl.glColor3fv(COLOR_SELECTION);
        }

        public void PopSelectionColor()
        {
            if (_selectionColorStack == null)
                return;

            if (_selectionColorStack.IsColorMaterial)
            {
                Gl.glEnable(Gl.GL_COLOR_MATERIAL);
                Gl.glColor3fv(_selectionColorStack.Color);
            }
            else
            {
                Gl.glDisable(Gl.GL_COLOR_MATERIAL);
                Gl.glMaterialfv(Gl.GL_FRONT_AND_BACK, Gl.GL_AMBIENT, _selectionColorStack.Ambient);
                Gl.glMaterialfv(Gl.GL_FRONT_AND_BACK, Gl.GL_DIFFUSE, _selectionColorStack.Diffuse);
                Gl.glMaterialfv(Gl.GL_FRONT_AND_BACK, Gl.GL_SPECULAR, _selectionColorStack.Specular);
                Gl.glMaterialfv(Gl.GL_FRONT_AND_BACK, Gl.GL_EMISSION, _selectionColorStack.Emissive);
            }

            _selectionColorStack = null;
        }

        #endregion

        #region Helpers : PushMatrix / PopMatrix / LoadMatrix

        private void PushMatrix(float[] matrix)
        {
            _matrices.Push(matrix);
        }

        private void PopMatrix()
        {
            _matrices.Pop();
        }

        private void LoadMatrix(float[] matrix)
        {
            if (_matrices.Count == 0)
            {
                Gl.glLoadMatrixf(matrix);
                return;
            }

            Gl.glLoadIdentity();
            float[][] matrices = _matrices.ToArray();
            for (int index = 0; index < matrices.Length; index++)
                Gl.glMultMatrixf(matrices[index]);
        }

        #endregion

        #region Reset...

        // Default values from :
        // http://publib.boulder.ibm.com/infocenter/systems/index.jsp?topic=/com.ibm.aix.opengl/doc/openglrf/glLight.htm
        private void ResetLight(int lightId)
        {
            float[] zero = new float[] { 0f, 0f, 0f, 1f };
            Gl.glLightfv(lightId, Gl.GL_AMBIENT, zero);
            Gl.glLightfv(lightId, Gl.GL_DIFFUSE, zero);
            Gl.glLightfv(lightId, Gl.GL_SPECULAR, zero);
            Gl.glLightfv(lightId, Gl.GL_POSITION, new float[] { 0f, 0f, 1f, 0f });

            Gl.glLightfv(lightId, Gl.GL_SPOT_DIRECTION, new float[] { 0f, 0f, -1f });
            Gl.glLightf(lightId, Gl.GL_SPOT_CUTOFF, 180);
            Gl.glLightf(lightId, Gl.GL_SPOT_EXPONENT, 0);

            Gl.glLightf(lightId, Gl.GL_CONSTANT_ATTENUATION, 1);
            Gl.glLightf(lightId, Gl.GL_LINEAR_ATTENUATION, 0);
            Gl.glLightf(lightId, Gl.GL_QUADRATIC_ATTENUATION, 0);
        }

        private void ResetMaterial()
        {
            Gl.glMaterialfv(Gl.GL_FRONT_AND_BACK, Gl.GL_AMBIENT, new float[] { 0.2f, 0.2f, 0.2f, 1f });
            Gl.glMaterialfv(Gl.GL_FRONT_AND_BACK, Gl.GL_DIFFUSE, new float[] { 0.8f, 0.8f, 0.8f, 1f });
            Gl.glMaterialfv(Gl.GL_FRONT_AND_BACK, Gl.GL_SPECULAR, new float[] { 0f, 0f, 0f, 1f });
            Gl.glMaterialfv(Gl.GL_FRONT_AND_BACK, Gl.GL_EMISSION, new float[] { 0f, 0f, 0f, 1f });
            Gl.glMaterialf(Gl.GL_FRONT_AND_BACK, Gl.GL_SHININESS, 0);
        }

        #endregion

        #region NEXT : Multipass

        private void Multipass()
        {
            // three passes:
            // 1) first is diffuse texture + diffuse lighting
            // 2) second is specular highlight + gloss map 
            // 3) third is emissive texture.
            /*
            // Diffuse pass
            glDepthFunc(GL_LESS);
            glEnable(GL_TEXTURE_2D);
            glEnable(GL_LIGHTING);
            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
            glBindTexture(GL_TEXTURE_2D, diffuse_texture);
            // Note: the diffuse texture is of internal format GL_RGB //
            RenderObject();

            // Specular pass
            glDepthFunc(GL_EQUAL);
            glEnable(GL_LIGHTING);
            glEnable(GL_TEXTURE_2D);
            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); //GL_REPLACE);
            glBindTexture(GL_TEXTURE_2D, gloss_texture);
            // Gloss texture (just an alpha channel) now in internal format GL_LUMINANCE
            glEnable(GL_BLEND);
            glBlendFunc(GL_SRC_ALPHA, GL_ONE);
            RenderObject();

            // Emissive pass
            glDepthFunc(GL_EQUAL);
            glDisable(GL_LIGHTING);
            glEnable(GL_TEXTURE_2D);
            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
            glBindTexture(GL_TEXTURE_2D), emissive_texture);
            // Note: the emissive texture is of internal format GL_RGBA //
            glEnable(GL_BLEND);
            glBlendFunc(GL_SRC_ALPHA, GL_ONE);
            RenderObject();
            */
        }

        #endregion

    }

    #region enum : GLModeSettings

    sealed public class GLModeSettings
    {
        public bool GL_LIGHTING = false;
        public bool GL_COLOR_MATERIAL = false;
        public bool GL_TEXTURE = false;
    }

    #endregion

    #region GLShadingItem

    sealed class GLShadingItem
    {
        public bool IsColorMaterial;

        public float[] Color;

        public float[] Ambient;
        public float[] Diffuse;
        public float[] Emissive;
        public float[] Specular;

        public GLShadingItem(bool isColorMaterial)
        {
            IsColorMaterial = isColorMaterial;

            if (IsColorMaterial)
                Color = new float[4];
            else
            {
                Ambient = new float[4];
                Diffuse = new float[4];
                Emissive = new float[4];
                Specular = new float[4];
            }
        }
    }

    #endregion

    #region GlDrawingAI

    /// <summary>
    /// This class contains information used by the OpenGL renderer.
    /// We have one instance per geometry.
    /// </summary>
    sealed class GlDrawingAI : RSAssociatedInfo
    {
        static public int INFOTYPEID = 10000;

        public uint geometryVertexVBO = 0;
        public uint geometryNormalVBO = 0;
        public uint geometryIndiceVBO = 0;

        public uint normalVBO = 0;

        public GlDrawingAI()
        {
            InfoTypeId = 10000;
        }
    }

    #endregion

}
